// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Jeff Stewart
// Mac-specific code for LocalHostProbe

#include <Gestalt.h>

#include <CFArray.h>
#include <CFDate.h>
#include <CFPreferences.h>
#include <CFTimeZone.h>
#include <MacLocales.h>

#include <QtCore/QSysInfo>

#include "probes/localhost.h"

namespace popdiag {

namespace {

QString QStringFromCFStringRef(CFStringRef ref) {
  char buffer[256];
  CFStringGetCString(ref, buffer, sizeof(buffer), kCFStringEncodingUTF8);
  return QString::fromUtf8(buffer);
}

// A smart pointer for CF references.  Unfortunately, Core Foundation
// doesn't generally expose the types for the things the pointers at.
// So if we're working with a Foo, we have a typedef CFFooRef, but no
// CFFoo.  So we have to express this in terms of the
template<typename PtrType> class CFHolder {
 public:
  explicit CFHolder(PtrType ptr) : ptr_(ptr) {
  }
  ~CFHolder() {
    if (ptr_ != NULL) {
      CFRelease(ptr_);
    }
  }

  PtrType Get() { return ptr_; }
 private:
  PtrType ptr_;
};

}  // anonymous namespace

QString LocalHostProbe::GetTimezoneName() {
  CFHolder<CFTimeZoneRef> timezone(CFTimeZoneCopyDefault());
  QString rval = QStringFromCFStringRef(CFTimeZoneGetName(timezone.Get()));
  return rval;
}

int LocalHostProbe::GetOffsetFromGMTInMinutes() {
  CFHolder<CFTimeZoneRef> timezone(CFTimeZoneCopyDefault());
  // This follows the "CF Get" rule, so we don't own it.
  // Thus, no CFHolder is required for when:
  CFAbsoluteTime when = CFAbsoluteTimeGetCurrent();
  CFTimeInterval interval_in_seconds =
      CFTimeZoneGetSecondsFromGMT(timezone.Get(), when);
  return interval_in_seconds / 60;
}

void LocalHostProbe::LogLocale() {
  CFHolder<CFLocaleRef> locale(CFLocaleCopyCurrent());

  // This will be in the local language, not in English.
  // Follows the "CF Get" rule, so we don't own it.
  // Thus, no CFHolder is required for country_ref:
  CFTypeRef country_ref = CFLocaleGetValue(locale.Get(), kCFLocaleCountryCode);
  if (CFGetTypeID(country_ref) == CFStringGetTypeID()) {
    QString country = QStringFromCFStringRef(
        reinterpret_cast<CFStringRef>(country_ref));
    LogDetails("Country", true, country);
  }

  // Log the interface default language:
  CFHolder<CFPropertyListRef> langs(CFPreferencesCopyAppValue(
      CFSTR("AppleLanguages"),
      kCFPreferencesCurrentApplication));
  if (CFGetTypeID(langs.Get()) == CFArrayGetTypeID()) {
    // langs retains memory ownership underlying langs_array:
    CFArrayRef langs_array = reinterpret_cast<CFArrayRef>(langs.Get());
    QString language = QStringFromCFStringRef(
        reinterpret_cast<CFStringRef>(CFArrayGetValueAtIndex(langs_array, 0)));
    LogDetails("Language", true, language);
  }
}

void LocalHostProbe::LogOSVersion() {
  QString os = QString("Mac OS ???? (%1)").arg(QSysInfo::MacintoshVersion);
  switch (QSysInfo::MacintoshVersion) {
  case QSysInfo::MV_9:
    os = "Mac OS 9";
    break;
  case QSysInfo::MV_10_0:
    os = "Mac OS 10.0 (Cheetah)";
    break;
  case QSysInfo::MV_10_1:
    os = "Mac OS 10.1 (Puma)";
    break;
  case QSysInfo::MV_10_2:
    os = "Mac OS 10.2 (Jaguar)";
    break;
  case QSysInfo::MV_10_3:
    os = "Mac OS 10.3 (Panther)";
    break;
  case QSysInfo::MV_10_4:
    os = "Mac OS 10.4 (Tiger)";
    break;
  case QSysInfo::MV_10_5:
    os = "Mac OS 10.5 (Leopard)";
    break;
  case QSysInfo::MV_Unknown:
    // Do nothing, keep the default from above. We do not use
    // a "default" entry here so that the compiler warns us if
    // we do not handle a new version.
    ;
  }
  LogDetails("OS", true, os);

  long processor = 0;
  Gestalt(gestaltSysArchitecture, &processor);

  QString processor_string;
  switch (processor) {
  case gestaltIntel:
    processor_string = "Intel";
    break;
  case gestalt68k:
    processor_string = "68K";
    break;
  case gestaltPowerPC:
    processor_string = "PPC";
    break;
  default:
    processor_string = QString("???%1").arg(processor);
    break;
  }
  LogDetails("Processor", true, processor_string);
}

}  // namespace popdiag
